/*
 *  Arrow
 *
 *  Created by David Giovannini on 1/5/07.
 *  Copyright 2007 Software by Jove. All rights reserved.
 *
 */
 
#ifndef SBJ_Arrow_Dispatcher_h
#define SBJ_Arrow_Dispatcher_h

#include "SBJ/Arrow/IDispatcher.h"
#include "SBJ/Thread/TaskDispatcher.h"
#include "SBJ/Thread/Blocker.h"

#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/noncopyable.hpp>

#include <hash_map>
#include <hash_set>

namespace SBJ
{

namespace Arrow
{

class Dispatcher : public IDispatcher, private boost::noncopyable
{
public:
	typedef boost::shared_ptr< Dispatcher > Ptr;

	Dispatcher( int numDispatchingThreads );
	
	virtual ~Dispatcher( void );
	
	virtual void onMessage( Message& msg );

	virtual bool subscribe( const Subscription& subscription );
	
	virtual bool unsubscribe( const Subscription& subscription );
	
	virtual Stats getDispatchStats( void ) const { return m_stats.getResults(); }

	virtual void resetStats( void ) { m_stats.reset(); }

private:
	typedef stdext::hash_set< void* > Subscribers;
	typedef stdext::hash_map< Topic, Subscribers > KeyedSubscribers;
	typedef stdext::hash_set< Topic > Topics;
	typedef stdext::hash_map< void*, Topics > KeyedTopics;

	class Dispatch
	{
	public:
		// execute
		Dispatch( Message* msg );

		// End Visiting
		Dispatch( Message* msg, Blocker& blocker );

		// Visit
		Dispatch( Message* msg, Blocker& blocker, void* subscriber );

		// Perform
		void operator () ( void );

	private:
		Message* m_msg;
		Blocker m_blocker;
		void* m_subscriber;
	};
	
	boost::mutex m_subscriberGuard;
	KeyedSubscribers m_byTopic;
	KeyedTopics m_bySubscriber;
	TaskDispatcher< Dispatch > m_threadPool;
	const int m_numThreads;
	DispatchStats m_stats;

	KeyedSubscribers::iterator m_workByTopicIter;
	enum ETask
	{
		task_add,
		task_remove
	};
	struct Work
	{
		ETask task;
		void* subscriber;
		Topic topic;
	};
	std::list< Work > m_delayedTasks;
	
	bool subscribe( void* subscriber, const Topic& topic );
	
	bool unsubscribe( void* subscriber, const Topic& topic );

	void performTasks( void );
};

}

}

#endif